﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace STSdb.MTL
{
    /// <summary>
    /// This object includes class Transaction (visible for the user) and holds internally technical transaction information.
    /// </summary>
    public class TransactionUnit:IDisposable
    {
        TransactionsCoordinator _transactionsCoordinator = null;      
        /// <summary>
        /// Represents object which will be used by user, there we have all query methods, depending upon query we now if the table has to be locked or not.
        /// </summary>
        Transaction _transaction = null;

        public TransactionUnit(TransactionsCoordinator transactionsCoordinator)
        {
            this._transactionsCoordinator = transactionsCoordinator;            
            this._transaction = new Transaction(this);           
        }

        public Transaction Transaction
        {
            get { return this._transaction; }
        }       

        public int TransactionThreadId
        {
            get { return this._transaction.ManagedThreadId; }
        }

        public TransactionsCoordinator TransactionsCoordinator
        {
            get { return this._transactionsCoordinator; }
        }


        /// <summary>
        /// Lock for all tables definitions inside current transaction
        /// </summary>
        ReaderWriterLockSlim _sync_transactionWriteTables = new ReaderWriterLockSlim();

        /// <summary>
        /// It holds all tables marked for possible mutations
        /// </summary>
        Dictionary<string, ITransactable> _transactionWriteTables = new Dictionary<string, ITransactable>();

        /// <summary>
        /// List of tables which are waiting for reservation for writing, we need it to predict deadlock situations.
        /// </summary>
        List<string> _transactionWriteTablesAwaitingReservation = new List<string>();
              

        /// <summary>
        /// Is called when transaction is finished.
        /// For every WRITE-TABLE inside of the transactions will be called TransactionIsFinished method.
        /// </summary>
        public void Dispose()
        {            

            _sync_transactionWriteTables.EnterReadLock();
            try
            {
                foreach (var tt in _transactionWriteTables.Where(r=>r.Value != null))
                {
                    tt.Value.TransactionIsFinished(this._transaction.ManagedThreadId);
                }
            }
            catch (System.Exception ex)
            {
                throw ex;
            }
            finally
            {
                _sync_transactionWriteTables.ExitReadLock();
            }

            //clearing all transaction tables

            _sync_transactionWriteTables.EnterWriteLock();
            try
            {
                _transactionWriteTables.Clear();    //holds table name + ITransactable interface                
                _transactionWriteTablesAwaitingReservation.Clear(); //holds tables with awaiting reservation, means that transaction thread is blocked
            }
            catch (System.Exception ex)
            {
                throw ex;
            }
            finally
            {
                _sync_transactionWriteTables.ExitWriteLock();
            }
        }


        #region "Tables Awaiting "WRITE" reservation "
        public void AddTransactionWriteTablesAwaitingReservation(List<string> tablesNames)
        {
            _sync_transactionWriteTables.EnterWriteLock();
            try
            {
                _transactionWriteTablesAwaitingReservation.AddRange(tablesNames.Except(_transactionWriteTablesAwaitingReservation));
            }
            finally
            {
                _sync_transactionWriteTables.ExitWriteLock();
            }
        }

        public void ClearTransactionWriteTablesAwaitingReservation(List<string> tablesNames)
        {
            _sync_transactionWriteTables.EnterWriteLock();
            try
            {
                _transactionWriteTablesAwaitingReservation.RemoveAll(r => tablesNames.Contains(r));
            }
            finally
            {
                _sync_transactionWriteTables.ExitWriteLock();
            }
        }

        public List<string> GetTransactionWriteTablesAwaitingReservation()
        {
            _sync_transactionWriteTables.EnterReadLock();
            try
            {
                return _transactionWriteTablesAwaitingReservation;
            }
            finally
            {
                _sync_transactionWriteTables.ExitReadLock();
            }
        }
        #endregion


        /// <summary>
        /// Adds a table which will take place in transaction operations
        /// </summary>
        /// <param name="tableName"></param>
        /// <param name="table">if null - will be added to Reservation table</param>
        public void AddTransactionWriteTable(string tableName, ITransactable table)
        {

            _sync_transactionWriteTables.EnterWriteLock();
            try
            {

                if (!_transactionWriteTables.ContainsKey(tableName))
                {
                    this._transactionWriteTables.Add(tableName, table);
                }
                else
                {
                    if (_transactionWriteTables[tableName] == null)
                        _transactionWriteTables[tableName] = table;
                }
                
            }
            catch (System.Exception ex)
            {
                throw ex;
            }
            finally
            {
                _sync_transactionWriteTables.ExitWriteLock();
            }
        }

        /// <summary>
        /// Returns all tables which took place in write operation for the current transaction
        /// </summary>
        /// <returns></returns>
        public List<ITransactable> GetTransactionWriteTables()
        {
            _sync_transactionWriteTables.EnterReadLock();
            try
            {
                return _transactionWriteTables.Where(r=>r.Value != null).Select(r=>r.Value).ToList();
            }
            catch (System.Exception ex)
            {
                throw ex;
            }
            finally
            {
                _sync_transactionWriteTables.ExitReadLock();
            }

        }

        /// <summary>
        /// Get ITransactable by tableName.
        /// Can return NULL, if table is not in a list of WRITE tables.
        /// </summary>
        /// <param name="tableName"></param>
        /// <returns></returns>
        public ITransactable GetTransactionWriteTable(string tableName)
        {
            _sync_transactionWriteTables.EnterReadLock();
            try
            {
                return _transactionWriteTables.Where(r => r.Value != null && r.Value.TableName.Equals(tableName,StringComparison.OrdinalIgnoreCase)).Select(r => r.Value).FirstOrDefault();
            }
            catch (System.Exception ex)
            {
                throw ex;
            }
            finally
            {
                _sync_transactionWriteTables.ExitReadLock();
            }

        }

        /// <summary>
        /// Returns only table names for reservation
        /// </summary>
        /// <returns></returns>
        public List<string> GetTransactionWriteTablesNames()
        {
            _sync_transactionWriteTables.EnterReadLock();
            try
            {
                return _transactionWriteTables.Keys.ToList();             
            }
            catch (System.Exception ex)
            {
                throw ex;
            }
            finally
            {
                _sync_transactionWriteTables.ExitReadLock();
            }

        }


        /// <summary>
        /// 
        /// </summary>
        public void Commit()
        {
            //calling for each table write table commit
            _sync_transactionWriteTables.EnterReadLock();
            try
            {
                foreach (var tt in _transactionWriteTables.Where(r => r.Value != null))
                {
                    tt.Value.Commit();
                }
            }
            catch (System.Exception ex)
            {
                throw ex;
            }
            finally
            {
                _sync_transactionWriteTables.ExitReadLock();
            }
        }

        public void RollBack()
        {
            //calling for each table write table rollback
            _sync_transactionWriteTables.EnterReadLock();
            try
            {
                foreach (var tt in _transactionWriteTables.Where(r => r.Value != null))
                {
                    tt.Value.RollBack();
                }
            }
            catch (System.Exception ex)
            {
                throw ex;
            }
            finally
            {
                _sync_transactionWriteTables.ExitReadLock();
            }
        }


    }
}
